package com.translate.web.config.common.excecption;

import java.util.Map;
import java.util.Objects;

import javax.servlet.http.HttpServletRequest;

import org.springframework.dao.DataAccessException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.NoHandlerFoundException;

import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.response.enums.ErrorCode;
import com.response.vo.EmptyBody;
import com.response.vo.ResponseModel;
import com.translate.web.config.common.excecption.runtime.SystemRuntimeException;

//@ControllerAdvice
public class GlobalExceptionHandler {

	@ExceptionHandler(value = BindException.class)
	@ResponseBody
	public ResponseModel<EmptyBody> resolveException(BindException ex) {
		BindingResult bindingResult = ex.getBindingResult();
		Object errorField = "";
		String errorCode;
		if (bindingResult.hasFieldErrors()) {
			errorCode = Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage();
			errorField = String.format("Field: %s", bindingResult.getFieldError().getField());
		} else {
			errorCode = Objects.requireNonNull(bindingResult.getGlobalError()).getDefaultMessage();
		}

		return resolveCode(errorCode, errorField);
	}

	@ExceptionHandler(value = MethodArgumentNotValidException.class)
	@ResponseBody
	public ResponseModel<EmptyBody> resolveException(MethodArgumentNotValidException ex) {
		BindingResult bindingResult = ex.getBindingResult();
		Object errorField = "";
		String errorCode;
		if (bindingResult.hasFieldErrors()) {
			errorCode = Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage();
			errorField = String.format("Field: %s", bindingResult.getFieldError().getField());
		} else {
			errorCode = Objects.requireNonNull(bindingResult.getGlobalError()).getDefaultMessage();
		}

		return resolveCode(errorCode, errorField);
	}

	@ExceptionHandler(value = HttpMessageNotReadableException.class)
	@ResponseBody
	public ResponseModel<EmptyBody> resolveException(HttpMessageNotReadableException ex) {
		Throwable cause = ex.getCause();
		if (cause instanceof InvalidFormatException) {
			JsonMappingException.Reference ref = ((InvalidFormatException) cause).getPath().stream().findFirst()
					.orElse(null);
			return resolveCode(ErrorCode.RESULT_CODE_ILLEGALPARAMETER,
					new Object[] { String.format("Field: %s", ref != null ? ref.getFieldName() : "unknown") });
		} else {
			return resolveCode(ErrorCode.RESULT_CODE_ILLEGALPARAMETER);
		}
	}

	@ExceptionHandler(value = HttpMediaTypeNotSupportedException.class)
	@ResponseBody
	public ResponseModel<EmptyBody> resolveException(HttpMediaTypeNotSupportedException ex) {
		return resolveCode(ErrorCode.RESULT_CODE_REQUEST_ERROR, "无效的请求方法");
	}

	@ExceptionHandler(value = DataAccessException.class)
	@ResponseBody
	public ResponseModel<EmptyBody> resolveException(DataAccessException ex) {
		System.out.println(ex.getMessage());
		return resolveCode(ErrorCode.RESULT_CODE_DATABASE_ERROR);
	}

	@ExceptionHandler(SystemRuntimeException.class)
	@ResponseBody
	public ResponseModel<EmptyBody> resolveException(SystemRuntimeException ex, HttpServletRequest request) {
//        log.info(ex.toString());
//        log.info(LocaleContextHolder.getLocale().toString());
		return resolveCode(ex.getMessageCode(), ex.getMessage());
	}

	@ExceptionHandler(value = ClassCastException.class)
	@ResponseBody
	public ResponseModel<EmptyBody> resolveException(ClassCastException ex) {
//        log.warn(ex.getMessage());
		return resolveCode(ErrorCode.RESULT_CODE_UNKNOWN_ERROR);
	}

	@ExceptionHandler(value = NullPointerException.class)
	@ResponseBody
	public ResponseModel<EmptyBody> resolveException(NullPointerException ex) {
//        log.warn(ex.getMessage());
		return resolveCode(ErrorCode.RESULT_CODE_UNKNOWN_ERROR);
	}

	@ExceptionHandler(MaxUploadSizeExceededException.class)
	@ResponseBody
	public ResponseModel<EmptyBody> resolveException(MaxUploadSizeExceededException ex) {
		return resolveCode(ErrorCode.RESULT_CODE_FILE_TOO_LARGE);
	}

	@ExceptionHandler(RuntimeException.class)
	@ResponseBody
	public ResponseModel<EmptyBody> resolveException(RuntimeException ex) {
//        log.info(ex.toString());
		return resolveCode(ErrorCode.RESULT_CODE_UNKNOWN_ERROR);
	}

	@ExceptionHandler(value = NoHandlerFoundException.class)
	@ResponseBody
	public ResponseModel<EmptyBody> resolveException(NoHandlerFoundException ex) {
		return resolveCode(ErrorCode.RESULT_CODE_REQUEST_ERROR);
	}

	@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
	@ResponseBody
	public ResponseModel<EmptyBody> resolveException(HttpRequestMethodNotSupportedException ex) {
		return resolveCode(ErrorCode.RESULT_CODE_REQUEST_ERROR);
	}

	@ExceptionHandler(value = Exception.class)
	@ResponseBody
	public ResponseModel<EmptyBody> resolveException(Exception ex) {
		return resolveCode(ErrorCode.RESULT_CODE_UNKNOWN_ERROR);
	}

	private ResponseModel<EmptyBody> resolveCode(String code, String message) {
		return ResponseModel.error(code, message);
	}

	private ResponseModel<EmptyBody> resolveCode(String code, Object... errorArgs) {
		return ResponseModel.error(code, errorArgs);
	}

	private ResponseModel<EmptyBody> resolveCode(String code) {
		return ResponseModel.error(code);
	}

	private ResponseModel<Map<String, Object>> resolveCode(String code, Map<String, Object> data) {
		return ResponseModel.error(data, code);
	}
}
